package com.yh.mybatis.extention.plugin;

import com.yh.mybatis.extention.plugin.mapper.YhXmlElementGenerator;
import com.yh.mybatis.extention.plugin.mapper.YhJavaMapperMethodGenerator;
import org.mybatis.generator.api.CommentGenerator;
import org.mybatis.generator.api.FullyQualifiedTable;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.PluginAdapter;
import org.mybatis.generator.api.dom.java.*;
import org.mybatis.generator.api.dom.xml.Document;
import org.mybatis.generator.codegen.mybatis3.javamapper.elements.AbstractJavaMapperMethodGenerator;
import org.mybatis.generator.codegen.mybatis3.xmlmapper.elements.AbstractXmlElementGenerator;
import org.mybatis.generator.internal.JDBCConnectionFactory;

import java.sql.*;
import java.util.*;

import static org.mybatis.generator.internal.util.StringUtility.isTrue;

/**
 * 自定义代码生成器
 * @author liubing
 */
public class YhMybatisPlugin extends PluginAdapter {

    /**
     * 序列化
     */
    private FullyQualifiedJavaType serializable;
    /**
     *
     */
    private boolean useToStringFromRoot;

    private static final String SERIAL_VERSION_UID = "serialVersionUID";

    private static final String INDEX_NAME = "INDEX_NAME";

    private static final String INDEX_TYPE = "TYPE";

    private static final String COLUMN_NAME = "COLUMN_NAME";
    public YhMybatisPlugin() {
        super();
        serializable = new FullyQualifiedJavaType("java.io.Serializable");
        useToStringFromRoot = isTrue(properties.getProperty("useToStringFromRoot"));
    }

    @Override
    public boolean validate(List<String> warnings) {
        return true;
    }
    /**
     * 生成自定义实体
     */
    @Override
    public boolean modelBaseRecordClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        addSerialVersionUID(topLevelClass, introspectedTable);
        generateToString(introspectedTable,topLevelClass);
        return super.modelBaseRecordClassGenerated(topLevelClass, introspectedTable);
    }

    @Override
    public boolean modelExampleClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        return super.modelExampleClassGenerated(topLevelClass, introspectedTable);
    }

    @Override
    public boolean sqlMapDocumentGenerated(Document document, IntrospectedTable introspectedTable) {
        FullyQualifiedTable table = introspectedTable.getFullyQualifiedTable();
        List<String> columnIndexs = getIndexColumnList(table);
        AbstractXmlElementGenerator elementGenerator = new YhXmlElementGenerator(columnIndexs);
        elementGenerator.setContext(context);
        elementGenerator.setIntrospectedTable(introspectedTable);
        elementGenerator.addElements(document.getRootElement());
        return super.sqlMapDocumentGenerated(document, introspectedTable);
    }


    @Override
    public boolean clientGenerated(Interface interfaze, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        AbstractJavaMapperMethodGenerator methodGenerator = new YhJavaMapperMethodGenerator();
        methodGenerator.setContext(context);
        methodGenerator.setIntrospectedTable(introspectedTable);
        methodGenerator.addInterfaceElements(interfaze);
        return super.clientGenerated(interfaze, topLevelClass, introspectedTable);
    }

    /**
     * toString 生成
     * @param introspectedTable
     * @param topLevelClass
     */
    private void generateToString(IntrospectedTable introspectedTable,
                                  TopLevelClass topLevelClass) {
        Method method = new Method();
        method.setVisibility(JavaVisibility.PUBLIC);
        method.setReturnType(FullyQualifiedJavaType.getStringInstance());
        method.setName("toString");
        if (introspectedTable.isJava5Targeted()) {
            method.addAnnotation("@Override");
        }

        context.getCommentGenerator().addGeneralMethodComment(method,
                introspectedTable);

        method.addBodyLine("StringBuilder sb = new StringBuilder();");
        method.addBodyLine("sb.append(getClass().getSimpleName());");
        method.addBodyLine("sb.append(\" [\");");
        method.addBodyLine("sb.append(\"Hash = \").append(hashCode());");
        StringBuilder sb = new StringBuilder();
        for (Field field : topLevelClass.getFields()) {
            String property = field.getName();
            if(property.equals(SERIAL_VERSION_UID)){
                continue;
            }
            sb.setLength(0);
            sb.append("sb.append(\"").append(", ").append(property)
                    .append("=\")").append(".append(").append(property)
                    .append(");");
            method.addBodyLine(sb.toString());
        }

        method.addBodyLine("sb.append(\"]\");");
        if (useToStringFromRoot && topLevelClass.getSuperClass() != null) {
            method.addBodyLine("sb.append(\", from super class \");");
            method.addBodyLine("sb.append(super.toString());");
        }
        method.addBodyLine("return sb.toString();");
        topLevelClass.addMethod(method);
    }

    /**
     * 生成序列化
     * @param topLevelClass
     * @param introspectedTable
     */
    private void addSerialVersionUID(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        topLevelClass.addImportedType(serializable);
        topLevelClass.addSuperInterface(serializable);
        CommentGenerator commentGenerator = context.getCommentGenerator();
        Field field = new Field();
        field.setVisibility(JavaVisibility.PRIVATE);
        field.setType(new FullyQualifiedJavaType("long"));
        field.setStatic(true);
        field.setFinal(true);
        field.setName(SERIAL_VERSION_UID);
        StringBuilder stringBuilder = new StringBuilder();
        Random random = new Random(Integer.MAX_VALUE);
        stringBuilder.append(random.nextLong()).append("L");
        field.setInitializationString(stringBuilder.toString());
        commentGenerator.addFieldComment(field, introspectedTable);
        topLevelClass.addField(field);
    }

    /**
     * 获取索引列集合
     * //FullyQualifiedTable table = introspectedTable.getFullyQualifiedTable();
     * @param table 表名称
     * @return 表索引列集合
     */
    private List<String> getIndexColumnList(FullyQualifiedTable table) {
        List<String> columnIndexList = new ArrayList<>();
        Connection connection = null;
        Statement statement = null;
        ResultSet rs = null;
        try {
            JDBCConnectionFactory jdbc = new JDBCConnectionFactory(context.getJdbcConnectionConfiguration());
            connection = jdbc.getConnection();
            //使用DatabaseMetaData 对象获取数据源相关信息
            DatabaseMetaData dbmd=connection.getMetaData();
            //获取索引信息
            rs=dbmd.getIndexInfo(null,null,table.getIntrospectedTableName(),false,false);
            //输出索引信息
            Set<String> indexColumns = new LinkedHashSet<>();

            while(rs.next()){
                int index=rs.getInt(INDEX_TYPE);
                String columnName = rs.getString(COLUMN_NAME);
                //index 为0，表示非索引
                if(index !=0){
                    indexColumns.add(columnName);
                }
            }
            columnIndexList.addAll(indexColumns);
        } catch (SQLException e) {

        } finally {
            closeConnection(connection, statement, rs);
        }
        return columnIndexList;
    }

    /**
     * 关闭链接
     * @param connection
     * @param statement
     * @param rs
     */
    private void closeConnection(Connection connection, Statement statement, ResultSet rs) {
        try {
            if (null != rs) {
                rs.close();
            }
        } catch (SQLException e) {

            e.printStackTrace();
        } finally {
            try {
                if (statement != null) {
                    statement.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {

                try {
                    if (connection != null) {
                        connection.close();
                    }
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
